package com.heima.article.service.impl;

import com.heima.admin.feign.AdChannelFeign;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.mapper.ApAuthorMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.ApCollectionService;
import com.heima.behavior.feign.ApBehaviorEntryFeign;
import com.heima.behavior.feign.ApLikesBehaviorFeign;
import com.heima.behavior.feign.ApUnlikesBehaviorFeign;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.dtos.Result;
import com.heima.common.exception.AppHttpCodeEnum;
import com.heima.common.exception.LeadNewsException;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.dtos.ApArticleDto;
import com.heima.model.article.dtos.ApArticleInfoDto;
import com.heima.model.article.dtos.ApArticleVo;
import com.heima.model.article.dtos.ArticleBehaviorDto;
import com.heima.model.article.pojos.*;
import com.heima.model.behavior.pojos.ApBehaviorEntry;
import com.heima.model.behavior.pojos.ApLikesBehavior;
import com.heima.model.behavior.pojos.ApUnlikesBehavior;
import com.heima.model.user.pojos.ApUser;
import com.heima.model.user.pojos.ApUserFollow;
import com.heima.user.feign.ApUserFollowFeign;
import com.heima.utils.common.BeanHelper;
import com.heima.utils.common.JsonUtils;
import com.heima.utils.common.ThreadLocalUtils;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 文章信息表，存储已发布的文章 服务实现类
 * </p>
 *
 * @author heima
 * @since 2021-08-06
 */
@Service
@Transactional
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
    @Autowired
    private ApArticleMapper apArticleMapper;
    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;
    @Autowired
    private ApArticleContentMapper apArticleContentMapper;
    @Autowired
    private ApAuthorMapper apAuthorMapper;
    @Autowired
    private ApBehaviorEntryFeign apBehaviorEntryFeign;
    @Autowired
    private ApLikesBehaviorFeign apLikesBehaviorFeign;
    @Autowired
    private ApUnlikesBehaviorFeign apUnlikesBehaviorFeign;
    @Autowired
    private ApCollectionService apCollectionService;
    @Autowired
    private ApUserFollowFeign apUserFollowFeign;
    @Autowired
    private AdChannelFeign adChannelFenign;
    @Autowired
    private RedisTemplate redisTemplate;


    @Override
    public Result<List<ApArticle>> loadApArticle(ApArticleDto dto, int type) {
        if (dto.getMinBehotTime() == null) dto.setMinBehotTime(new Date());
        if (dto.getMaxBehotTime() == null) dto.setMaxBehotTime(new Date());

        try {
            List<ApArticle> articleList = apArticleMapper.loadApArticles(dto, type);

            return Result.ok(articleList);
        } catch (Exception e) {
            e.printStackTrace();
            throw new LeadNewsException(AppHttpCodeEnum.SERVER_ERROR);
        }


    }

    /**
     * 加载文章详情
     */
    @Override
    public Result<Map<String, Object>> loadArticleInfo(ApArticleInfoDto dto) {
        QueryWrapper<ApArticleConfig> configQueryWrapper = new QueryWrapper<>();
        configQueryWrapper.eq("article_id", dto.getArticleId());
        ApArticleConfig articleConfig = apArticleConfigMapper.selectOne(configQueryWrapper);

        if (articleConfig == null) {
            throw new LeadNewsException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if (articleConfig.getIsDown().equals(1) || articleConfig.getIsDelete().equals(1)) {
            throw new LeadNewsException(404, "文章已下架或被删除");
        }
        //2.查询文章内容
        QueryWrapper<ApArticleContent> contentQueryWrapper = new QueryWrapper<>();
        contentQueryWrapper.eq("article_id", dto.getArticleId());
        ApArticleContent articleContent = apArticleContentMapper.selectOne(contentQueryWrapper);
        if (articleContent == null) {
            throw new LeadNewsException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        try {
            Map<String, Object> result = new HashMap<>();
            result.put("config", articleConfig);
            result.put("content", articleContent);

            return Result.ok(result);
        } catch (Exception e) {
            e.printStackTrace();
            throw new LeadNewsException(AppHttpCodeEnum.SERVER_ERROR);
        }

    }

    /**
     * 查询文章的行为数据，进行回显
     */
    @Override
    public Result<Map<String, Boolean>> loadArticleBehavior(ArticleBehaviorDto dto) {
        Integer userId = null;
        ApUser apUser = (ApUser) ThreadLocalUtils.get();
        if (apUser != null) {
            userId = apUser.getId();
        }
        Integer equipmentId = dto.getEquipmentId();
        ApBehaviorEntry behaviorEntry = apBehaviorEntryFeign.findByUserIdOrEquimentId(userId, equipmentId);
        if (behaviorEntry == null) {
            return Result.ok();
        }
        try {
            Boolean isfollow=false,islike=false,isunlike=false,iscollection=false;
            //查询点赞记录
            ApLikesBehavior likeBehavoir = apLikesBehaviorFeign.findLike(behaviorEntry.getId(), dto.getArticleId());
            if (likeBehavoir != null && likeBehavoir.getOperation().equals(0)) {
                islike = true;
            }
            //查询不喜欢记录
            ApUnlikesBehavior unlikeBehavoir = apUnlikesBehaviorFeign.findUnlike(behaviorEntry.getId(), dto.getArticleId());
            if (unlikeBehavoir != null && unlikeBehavoir.getType().equals(0)) {
                isunlike = true;
            }
            //查询收藏记录
            ApCollection apCollection = apCollectionService.findCollection(behaviorEntry.getId(), dto.getArticleId());
            if (apCollection != null) {
                iscollection = true;
            }

            //查询关注记录
            //根据作者ID查找作者
            ApAuthor apAuthor = apAuthorMapper.selectById(dto.getAuthorId());
            if (apAuthor != null) {
                ApUserFollow userFollow = apUserFollowFeign.findUserFollow(userId, apAuthor.getUserId());
                if (userFollow != null) {
                    isfollow = true;
                }
            }
            Map<String, Boolean> result = new HashMap<>();
            result.put("isfollow",isfollow);
            result.put("iscollection",iscollection);
            result.put("islike",islike);
            result.put("isunlike",isunlike);
            return Result.ok(result);
        } catch (Exception e) {
            e.printStackTrace();
            throw new LeadNewsException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }
    @Override
    public void saveToRedis() {
        //1.查询前5天的文章
        QueryWrapper<ApArticle> queryWrapper = new QueryWrapper<>();
        //获取当前时间的前5天的时间
        String date = DateTime.now().minusDays(5).toString("yyyy-MM-dd HH:mm:ss");
        queryWrapper.ge("publish_time",date);
        List<ApArticle> apArticles = list(queryWrapper);

        //2.计算文章的分值
        List<ApArticleVo> apArticleVoList= computeArticleScore(apArticles);

        //3.查询所有频道
        List<AdChannel> channelList = (List<AdChannel>) adChannelFenign.findAll().getData();

        //4.找出每个频道的文章，按照分值倒序，截取前30条文章，缓存到redis
        if (CollectionUtils.isNotEmpty(channelList)){
            channelList.forEach(adChannel -> {
                List<ApArticleVo> apArticleVos = apArticleVoList.stream().
                        filter(apArticleVo -> apArticleVo.getChannelId().equals(adChannel.getId()))
                        .collect(Collectors.toList());
                String key = ArticleConstants.HOT_ARTICLE_FIRST_PAGE+adChannel.getId();
                sortAndCacheToRedis(apArticleVos,key);
            });
        }
        String key = ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.DEFAULT_TAG;
        //5.对默认频道进行缓存
        if (CollectionUtils.isNotEmpty(apArticleVoList)){
            sortAndCacheToRedis(apArticleVoList,key);
        }
    }
    /**
     * 对文章按照分值倒序，截取前30条文章，缓存到redis
     * @param articleVos
     */
    private void sortAndCacheToRedis(List<ApArticleVo> articleVos, String key) {
        if (CollectionUtils.isNotEmpty(articleVos)){
            articleVos.sort(((o1, o2) -> {
                return o2.getScore().compareTo(o1.getScore());
            }));
        }
        //截取前30条文章
        if (articleVos.size() > 30){
            articleVos = articleVos.subList(0, 30);
            //缓存到redis
            redisTemplate.opsForValue().set(key, JsonUtils.toString(articleVos));
        }


    }


    /**
     * 计算所有文章的分值
     * @param apArticles
     * @return
     */
    private List<ApArticleVo> computeArticleScore(List<ApArticle> apArticles) {
        List<ApArticleVo> apArticleVoList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(apArticles)){
            apArticles.forEach(apArticle -> {
                ApArticleVo apArticleVo = BeanHelper.copyProperties(apArticle, ApArticleVo.class);
                int score =  computeScore(apArticle);
                apArticleVo.setScore(score);
                apArticleVoList.add(apArticleVo);
            });
        }
        return apArticleVoList;
    }

    private int computeScore(ApArticle apArticle) {
        int score = 0;
        if (apArticle.getLikes() != null){
            score  += apArticle.getLikes()* ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if(apArticle.getViews()!=null){
            score += apArticle.getViews();
        }
        if(apArticle.getComment()!=null){
            score += apArticle.getComment()* ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if(apArticle.getCollection()!=null){
            score += apArticle.getCollection()* ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;

    }

}
